﻿/*--------------------------------------------------------------------------------
 * Adaptive Intelligence - Foundation Framework Class Library
 * Version 2.00
 * 
 * ZIP Information Header Base Class
 * 		Provides a base implementation for classes which represent file information
 * header data.	
 *------------------------------------------------------------------------------*/

/*--------------------------------------------------------------------------------
 * System Imports
 *------------------------------------------------------------------------------*/
using System;
using System.IO;
using Adaptive.Win32;

namespace Adaptive.Foundation.IO.Zip
{
    /// <summary>
    /// Provides a base implementation for classes which represent file information
    /// header data.
    /// </summary>
    /// <remarks>
    /// This class provides the same basic fields for the Zip local file header and the
    /// zip directory entry header.
    /// </remarks>
    public abstract class ZipInformationHeaderBase : AiObjectBase, IStorable
    {

        /*--------------------------------------------------------------------------------
         * Private Member Declarations
         *------------------------------------------------------------------------------*/
        #region Private Member Declarations
        //Entry signature value. 
        private byte[] _signature;
        //Version that created the item.
        private ZipCreatedByVersion _versionMadeBy;
        //Version needed to read the item.
        private ZipRequiredVersion _versionNeeded;
        //Bit flags.
        private ZipBitFlags _bitFlags;
        //Compression method.
        private CompressionMethods _compressionmethod = CompressionMethods.Deflated;
        //Last modified time.
        private ushort _lastModifiedTime;
        //Last modified date.
        private ushort _lastModifiedDate;
        //Cyclic redundancy check value.
        private uint _crc32;
        //Size of data when compressed.
        private int _compressedSize;
        //Size of data when uncompressed.
        private int _uncompressedSize;
        //Length of the file name value.
        private short _fileNameLEngth;
        //Length of the extra data field.
        private short _extraDataLength;
        //File name.
        private string _fileName = string.Empty;
        //Extra data field.
        private byte[] _extraData;
        #endregion

        /*--------------------------------------------------------------------------------
         * Constructor / Dispose / Destructor Methods
         *------------------------------------------------------------------------------*/
        #region Constructor / Dispose / Destructor Methods
        /// <summary>
        /// Initializes a new instance of the <see cref="ZipInformationHeaderBase" /> class.
        /// </summary>
        /// <remarks>
        /// This is the default constructor.
        /// </remarks>
        protected ZipInformationHeaderBase()
            : base()
        {
            _versionMadeBy = new ZipCreatedByVersion(ZipOriginationType.WindowsNTFS, 21);
            _versionNeeded = new ZipRequiredVersion(21);
            _bitFlags = new ZipBitFlags();
            _signature = CreateSignatureDefaultValue();

        }
        /// <summary>
        /// Releases unmanaged and - optionally - managed resources
        /// </summary>
        /// <param name="disposing"><b>true</b> to release both managed and unmanaged resources;
        /// <b>false</b> to release only unmanaged resources.</param>
        protected override void Dispose(bool disposing)
        {
            if ((!Disposed) && (disposing))
            {
                if (_extraData != null)
                    Array.Clear(_extraData, 0, _extraData.Length);
                if (_signature != null)
                    Array.Clear(_signature, 0, _signature.Length);
            }

            _signature = null;
            _fileName = null;
            _extraData = null;
            _bitFlags = null;
            _versionMadeBy = null;
            _versionNeeded = null;
            base.Dispose(disposing);
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Public Properties
         *------------------------------------------------------------------------------*/
        #region Public Properties
        /// <summary>
        /// Gets or sets the 4 byte signature value.
        /// </summary>
        /// <value>
        /// The signature value as a byte array.
        /// </value>
        public byte[] Signature
        {
            get
            {
                return _signature;
            }
            set
            {
                _signature = new byte[4];
                Array.Copy(value, _signature, 4);
            }
        }
        /// <summary>
        /// Gets the reference to the created-by version data.
        /// </summary>
        /// <value>
        /// A <see cref="ZipCreatedByVersion"/> instance containing the versioning data.
        /// </value>
        public ZipCreatedByVersion VersionMadeBy
        {
            get
            {
                return _versionMadeBy;
            }
        }
        /// <summary>
        /// Gets the reference to the required-to-read-file version data.
        /// </summary>
        /// <value>
        /// A <see cref="ZipRequiredVersion"/> instance containing the versioning data.
        /// </value>
        public ZipRequiredVersion VersionNeeded
        {
            get
            {
                return _versionNeeded;
            }
        }
        /// <summary>
        /// Gets the reference to the bit flags.
        /// </summary>
        /// <value>
        /// A <see cref="ZipBitFlags"/> instance used to read, write, and interpret
        /// the bit flags field.
        /// </value>
        public ZipBitFlags BitFlags
        {
            get
            {
                return _bitFlags;
            }
        }
        /// <summary>
        /// Gets or sets the method of compression used on the internal data.
        /// </summary>
        /// <value>
        /// A <see cref="CompressionMethod"/> enumerated value indicating the 
        /// type of compression algorithm used.
        /// </value>
        public CompressionMethods CompressionMethod
        {
            get
            {
                return _compressionmethod;
            }
            set
            {
                _compressionmethod = value;
            }
        }
        /// <summary>
        /// Gets or sets the last modified time value.
        /// </summary>
        /// <value>The last modified time value.</value>
        public ushort LastModifiedTime
        {
            get
            {
                return _lastModifiedTime;
            }
            set
            {
                _lastModifiedTime = value;
            }
        }
        /// <summary>
        /// Gets the last modified date and time value.
        /// </summary>
        /// <value>
        /// A <see cref="DateTime"/> value.
        /// </value>
        public DateTime LastModifiedDateAndTime
        {
            get
            {
                MsDosDate dosDate = new MsDosDate();
                dosDate.Date = _lastModifiedDate;
                dosDate.Time = _lastModifiedTime;

                return DateTime.FromFileTime(Win32.Kernel32.DosDateToFileTime(dosDate));
            }
        }
        /// <summary>
        /// Gets or sets the last modified date value.
        /// </summary>
        /// <value>The last modified date value.</value>
        public ushort LastModifiedDate
        {
            get
            {
                return _lastModifiedDate;
            }
            set
            {
                _lastModifiedDate = value;
            }
        }
        /// <summary>
        /// Gets or sets the cyclic-redundancy-check value.
        /// </summary>
        /// <value>
        /// The value of the CRC-32 calculation on the data.
        /// </value>
        public uint Crc32
        {
            get
            {
                return _crc32;
            }
            set
            {
                _crc32 = value;
            }
        }
        /// <summary>
        /// Gets or sets the size of the compressed data/
        /// </summary>
        /// <value>The size of the compressed data, in bytes.</value>
        public int CompressedSize
        {
            get
            {
                return _compressedSize;
            }
            set
            {
                _compressedSize = value;
            }
        }
        /// <summary>
        /// Gets or sets the size of the uncompressed data.
        /// </summary>
        /// <value>The size of the uncompressed data, in bytes.</value>
        public int UncompressedSize
        {
            get
            {
                return _uncompressedSize;
            }
            set
            {
                _uncompressedSize = value;
            }
        }
        /// <summary>
        /// Gets or sets the length of the file name.
        /// </summary>
        /// <value>The length of the file name.</value>
        public short FileNameLength
        {
            get
            {
                return _fileNameLEngth;
            }
            set
            {
                _fileNameLEngth = value;
            }
        }
        /// <summary>
        /// Gets or sets the length of the extra data, in bytes.
        /// </summary>
        /// <value>The length of the extra data.</value>
        public short ExtraDataLength
        {
            get
            {
                return _extraDataLength;
            }
            set
            {
                _extraDataLength = value;
            }
        }
        /// <summary>
        /// Gets or sets the name of the file.
        /// </summary>
        /// <value>The name of the file.</value>
        public string FileName
        {
            get
            {
                return _fileName;
            }
            set
            {

                if (String.IsNullOrEmpty(value))
                {
                    _fileNameLEngth = 0;
                    _fileName = string.Empty;
                }
                else
                {
                    _fileName = value;
                    _fileNameLEngth = Convert.ToInt16(value.Length);
                }
            }
        }
        /// <summary>
        /// Gets or sets the extra data.
        /// </summary>
        /// <value>
        /// The extra data as a byte array.
        /// </value>
        public byte[] ExtraData
        {
            get
            {
                return _extraData;
            }
            set
            {

                if (value == null)
                {
                    _extraData = null;
                }
                else
                {
                    _extraData = new byte[value.Length];
                    Array.Copy(value, _extraData, value.Length);
                }
            }
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Protected Methods
         *------------------------------------------------------------------------------*/
        #region Protected Methods
        /// <summary>
        /// Creates the 4 data bytes that make up the signature value used to identify
        /// the header.
        /// </summary>
        /// <returns>
        /// The signature value as a 4-byte array.
        /// </returns>
        protected abstract byte[] CreateSignatureDefaultValue();
        /// <summary>
        /// Reads the version-made-by versioning value.
        /// </summary>
        /// <param name="reader">
        /// An open <see cref="BinaryReader"/> used to read the contents 
        /// </param>
        /// <remarks>
        /// This allows different child implementations to read (or not read) and interpret
        /// this data as needed.
        /// </remarks>
        protected abstract void ReadVersionMadeBy(BinaryReader reader);
        /// <summary>
        /// Reads any fields not defined on the base class.
        /// </summary>
        /// <param name="reader">
        /// An open <see cref="BinaryReader"/> used to read the contents 
        /// </param>
        /// <remarks>
        /// This allows different child implementations to read (or not read) and interpret
        /// this data as needed.
        /// </remarks>
        protected abstract void ReadExtraFields(BinaryReader reader);
        /// <summary>
        /// Reads any remaining data in the structure.
        /// </summary>
        /// <param name="reader">
        /// An open <see cref="BinaryReader"/> used to read the contents 
        /// </param>
        /// <remarks>
        /// This allows different child implementations to read (or not read) and interpret
        /// this data as needed.
        /// </remarks>
        protected abstract void ReadRemainingData(BinaryReader reader);
        /// <summary>
        /// Writes the made-by versioning data.
        /// </summary>
        /// <param name="writer">
        /// An open <see cref="BinaryWriter"/> used to writer the contents 
        /// </param>
        /// <remarks>
        /// This allows different child implementations to write (or not write) 
        /// this data as needed.
        /// </remarks>
        protected abstract void WriteVersionMadeBy(BinaryWriter writer);
        /// <summary>
        /// Writes any fields not defined on the base class.
        /// </summary>
        /// <param name="writer">
        /// An open <see cref="BinaryWriter"/> used to writer the contents 
        /// </param>
        /// <remarks>
        /// This allows different child implementations to write (or not write) 
        /// this data as needed.
        /// </remarks>
        protected abstract void WriteExtraFields(BinaryWriter writer);
        /// <summary>
        /// Writes any remaining data in the structure.
        /// </summary>
        /// <param name="writer">
        /// An open <see cref="BinaryWriter"/> used to writer the contents 
        /// </param>
        /// <remarks>
        /// This allows different child implementations to write (or not write) 
        /// this data as needed.
        /// </remarks>
        protected abstract void WriteRemainingData(BinaryWriter writer);
        #endregion

        /*--------------------------------------------------------------------------------
         * Public Methods / Functions
         *------------------------------------------------------------------------------*/
        #region Public Methods / Functions
        /// <summary>
        /// Reads the content of the object from the underlying stream.
        /// </summary>
        /// <param name="reader">
        /// An open <see cref="BinaryReader"/> used to read the data content.
        /// </param>
        /// <returns>
        /// <b>true</b> if the operation is successful; otherwise, returns <b>false</b>.
        /// </returns>
        public virtual bool Load(BinaryReader reader)
        {
            byte[] bufferData = null;			//Data buffer.
            bool returnValue = false;		    //Return value.
            //PKZip - Shared elements between local file header and directory entry:
            //
            //Local File Header Signature	- 4 bytes.
            //Version						- 2 bytes.
            //General Purpose Bit Flag		- 2 bytes.
            //Compression Method				- 2 bytes.
            //Last Modified Time				- 2 bytes.
            //Last Modified Date				- 2 bytes.
            //CRC-32							- 4 bytes.
            //Compressed Size				- 4 bytes.
            //Uncompressed size				- 4 bytes.
            //File name length				- 2 bytes.
            //Extra data field length		- 2 bytes.
            //File name						- variable
            //Extra data field				- variable

            //Read the header data.
            try
            {
                _signature = reader.ReadBytes(4);
                ReadVersionMadeBy(reader);
                _versionNeeded.Value = reader.ReadInt16();
                _bitFlags.Value = reader.ReadInt16();
                _compressionmethod = (CompressionMethods)(reader.ReadInt16());
                _lastModifiedTime = reader.ReadUInt16();
                _lastModifiedDate = reader.ReadUInt16();
                _crc32 = reader.ReadUInt32();
                _compressedSize = reader.ReadInt32();
                _uncompressedSize = reader.ReadInt32();
                _fileNameLEngth = reader.ReadInt16();
                _extraDataLength = reader.ReadInt16();

                //Read any other defined fields.
                ReadExtraFields(reader);

                //Read the file name value.
                bufferData = reader.ReadBytes(_fileNameLEngth);
                _fileName = System.Text.ASCIIEncoding.ASCII.GetString(bufferData);

                //Read the extra field data.
                _extraData = reader.ReadBytes(_extraDataLength);

                //Read any data that is left.
                ReadRemainingData(reader);
                returnValue = true;
            }
            catch (IOException)
            {
            }

            return returnValue;

        }
        /// <summary>
        /// Writes the content of the object to the underlying stream.
        /// </summary>
        /// <param name="writer">
        /// An open <see cref="BinaryWriter"/> used to write the data content.
        /// </param>
        /// <returns>
        /// <b>true</b> if the operation is successful; otherwise, returns <b>false</b>.
        /// </returns>
        public virtual bool Save(BinaryWriter writer)
        {
            bool returnValue = false;		    //Return value

            //PKZip - Shared elements between local file header and directory entry:
            //
            //Local File Header Signature	- 4 bytes.
            //Version						- 2 bytes.
            //General Purpose Bit Flag		- 2 bytes.
            //Compression Method				- 2 bytes.
            //Last Modified Time				- 2 bytes.
            //Last Modified Date				- 2 bytes.
            //CRC-32							- 4 bytes.
            //Compressed Size				- 4 bytes.
            //Uncompressed size				- 4 bytes.
            //File name length				- 2 bytes.
            //Extra data field length		- 2 bytes.
            //File name						- variable
            //Extra data field				- variable

            //Write the header data.
            try
            {
                writer.Write(_signature);
                WriteVersionMadeBy(writer);
                writer.Write(_versionNeeded.Value);
                writer.Write(_bitFlags.Value);
                writer.Write((short)_compressionmethod);
                writer.Write(_lastModifiedTime);
                writer.Write(_lastModifiedDate);
                writer.Write(_crc32);
                writer.Write(_compressedSize);
                writer.Write(_uncompressedSize);
                writer.Write(_fileNameLEngth);
                writer.Write(_extraDataLength);

                //Write any other defined fields.
                WriteExtraFields(writer);

                //Write the file name value.
                writer.Write(System.Text.ASCIIEncoding.ASCII.GetBytes(_fileName));

                //Write the extra field data.
                if (_extraData != null)
                    writer.Write(_extraData);

                //Write any data that is left.
                WriteRemainingData(writer);

                writer.Flush();
                returnValue = true;
            }
            catch (IOException)
            {
            }

            return returnValue;
        }
        #endregion
    }
}
